<?php
// テスト用クラス
class myCls {
	public $beforeGet = false;
	public $afterGet = false;
	public $beforeSet = false;
	public $afterSet = false;
	public $callRes = null;
	
	function flgReset () {
		$this->beforeGet = false;
		$this->afterGet = false;
		$this->beforeSet = false;
		$this->afterSet = false;
	}
	
	function _beforeGet () {
		$this->beforeGet = true;
		
	}
	function _afterGet () {
		$this->afterGet = true;
	}
	function _beforeSet () {
		$this->beforeSet = true;
	}
	function _afterSet () {
		$this->afterSet = true;
	}
	
	function myMethod () {
		return 'method!';
	}
	
	function call ($str = null) {
		if ($str) $this->callRes = $str;
		return 'call!';
	}
	
}

// テスト用関数
function myFunc ($arg = null) {
	if ($arg) return $arg;
	return 'func!';
}
// factoryおよび型関係のテスト
class TestofYuToriFactory extends UnitTestCase {
	public function __construct () {
		$this->arrObj = array(
			'Null'=>null,
			'Scalar'=>'Hello World!',
			'Array'=>array('a', 'b', 'c'),
			'Object'=>new myCls(),
			'Bool'=>true,
		);
		
		// 試験を書き直すのが面倒なので、Rootオブジェクト代わりを作成しておく。
		$this->obj = YuTori::factory('test', null);
	}
	
	// factoryメソッドとgetTypeの動作確認。
	public function TestofFactory() {
		// type = auto
		foreach ($this->arrObj as $Key=>$Val) {
			$type = constant('YuTori::TYPE_'.strtoupper($Key));
			$obj = YuTori::factory($Key, $Val);
			$this->assertIdentical(get_class($obj), 'YuTori', 'factoryがセレクタを返していません。');
			$this->assertIdentical($obj->getType(), $type, 'factoryが'.$Key.'型を作成していません。'.$obj->getType());
		}

		// type指定
		foreach ($this->arrObj as $Key=>$Val) {
			$type = constant('YuTori::TYPE_'.strtoupper($Key));
			$obj = YuTori::factory($Key, $Val, $type);
			$this->assertIdentical(get_class($obj), 'YuTori', '型指定時、factoryがセレクタを返していません。');
			$this->assertIdentical($obj->getType(), constant('YuTori::TYPE_'.strtoupper($Key)), '型指定時、factoryが'.$Key.'型を作成していません。');
		}

		$obj = YuTori::factory('Function', Y::Func('myFunc'));
		$this->assertIdentical(get_class($obj), 'YuTori', '型指定時、factoryがセレクタを返していません。');
		$this->assertIdentical($obj->getType(), YuTori::TYPE_CALLABLE, '型指定時、factoryがfunction型を作成していません。');

		$obj = YuTori::factory('Function', array(new MyCls(), 'myMethod'), YuTori::TYPE_CALLABLE);
		$this->assertIdentical(get_class($obj), 'YuTori', '型指定時、メソッドを渡されたfactoryがセレクタを返していません。');
		$this->assertIdentical($obj->getType(), YuTori::TYPE_CALLABLE, '型指定時、メソッドを渡されたfactoryがfunction型を作成していません。');

		$obj = YuTori::factory('test', 'test', YuTori::TYPE_EMPTY);
		$this->assertIdentical(get_class($obj), 'YuTori', '型指定時、factoryがセレクタを返していません。');
		$this->assertIdentical($obj->getType(), YuTori::TYPE_EMPTY, '型指定時、factoryがempty型を作成していません。');

		$obj = YuTori::factory('test', 'test', 'abcdefg');
		$this->assertIdentical(get_class($obj), 'YuTori', '存在しない型指定時、factoryがセレクタを返していません。');
		$this->assertIdentical($obj->getType(), YuTori::TYPE_EMPTY, '存在しない型指定時、factoryがempty型を作成していません。');

		// YuToriを指定
		foreach ($this->arrObj as $Key=>$Val) {
			$obj = YuTori::factory($Key, $Val, $Key);
			$obj = YuTori::factory('test', $obj);
			$this->assertIdentical(get_class($obj), 'YuTori', 'factoryに'.$Key.'型YuToriを渡した時、セレクタを返していません。');
			$this->assertIdentical($obj->getType(), constant('YuTori::TYPE_'.strtoupper($Key)), 'factoryに'.$Key.'型YuToriを渡した時、'.$Key.'型を作成していません。');
		}
	}

	// getName、setNameの動作確認
	public function TestofName() {
		foreach ($this->arrObj as $Key=>$Val) {
			$obj = YuTori::factory($Key, $Val);
			$this->assertIdentical($obj->getName(), $Key, 'YuToriが名称'.$Key.'を保持していません。');
			$obj->setName ('newName');
			$this->assertIdentical($obj->getName(), 'newName', 'YuTori::setNameで名称'.$Key.'が変更されませんでした。');
			$obj->setName (null);
			$this->assertIdentical($obj->getName(), 'newName', 'YuTori::setNameにnull指定時、名称'.$Key.'が変更されました。');
		}
	}

	// get、setの動作確認
	public function TestofInstance() {
		$arrSub = $this->arrObj;
		// 基本パターンは全て組み合わせる
		foreach ($arrSub as $srcKey=>$srcVal) {
			$obj = YuTori::factory($srcKey, $srcVal);
			foreach ($this->arrObj as $dstKey=>$dstVal) {
				$obj = YuTori::factory($srcKey, $srcVal);
				$this->assertTrue($obj->set ($dstVal) ===	$obj, $srcKey.'型に'.$dstKey.'型をsetした際の戻り値が自分自身ではありません。');
				$this->assertIdentical($obj->get(), $dstVal, $srcKey.'型に'.$dstKey.'型をsetした際の保持インスタンスが間違っています。');
			}
		}
	}

	// addの動作確認。addParentsも内包される。children、parentsの基礎も。
	// ToDo: ここにcountの試験も追加する。
	public function TestofAdd() {
		$child = $this->obj->add ('null', null);
		$this->assertIdentical($child->get(), null, 'addにnull指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'null', 'addにnull指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('null')->pick() === $child->pick(), 'addにnull指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() === $this->obj->pick(), 'addにnull指定時、戻り値のparentが自分自身ではありません。');
		
		$child = $this->obj->add ('scalar', 'abc');
		$this->assertIdentical($child->get(), 'abc', 'addに"abc"指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'scalar', 'addに"abc"指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('scalar')->pick() === $child->pick(), 'addに"abc"指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() === $this->obj->pick(), 'addに"abc"指定時、戻り値のparentが自分自身ではありません。');
		
		// 同名上書き
		$child = $this->obj->add ('scalar', 123);
		$this->assertIdentical($child->get(), 123, 'addに123指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'scalar', 'addに123指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('scalar')->pick() === $child->pick(), 'addに123指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() === $this->obj->pick(), 'addに123指定時、戻り値のparentが自分自身ではありません。');
		
		$child = $this->obj->add ('array', array('a','b'));
		$this->assertTrue(is_array($child->get()), 'addに配列指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'array', 'addに配列指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('array')->pick() === $child->pick(), 'addに配列指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() ===
			$this->obj->pick(), 'addに配列指定時、戻り値のparentが自分自身ではありません。');
		
		$child = $this->obj->add ('object', $obj = new myCls ());
		$this->assertIdentical($child->get(), $obj,
		'addにオブジェクト指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'object',
		'addにオブジェクト指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('object')->pick()
			=== $child->pick(), 'addにオブジェクト指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() ===
			$this->obj->pick(), 'addにオブジェクト指定時、戻り値のparentが自分自身ではありません。');
		
		$child = $this->obj->add
			('function',YuTori::factory('test', 'myFunc', YuTori::TYPE_CALLABLE));
		$this->assertIdentical($child->get(), YuTori::TYPE_CALLABLE, 'addに関数指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'function', 'addに関数指定時、戻り値のgetNameの値が間違っています。');
		
		$this->assertTrue($this->obj->child('function')->pick() === $child->pick(), 'addに関数指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() === $this->obj->pick(), 'addに関数指定時、戻り値のparentが自分自身ではありません。');
		
		$child = $this->obj->add ('function',YuTori::factory('test', array(new myCls(),
			'myMethod'), YuTori::TYPE_CALLABLE) );
		$this->assertIdentical($child->get(), YuTori::TYPE_CALLABLE, 'addにメソッド指定時、戻り値のgetの値が間違っています。');
		$this->assertIdentical($child->getName(), 'function', 'addにメソッド指定時、戻り値のgetNameの値が間違っています。');
		
		$this->assertTrue($this->obj->child('function')->pick() === $child->pick(), 'addにメソッド指定時、childの戻り値とaddの戻り値が違います。');
		
		$this->assertTrue($child->parent($this->obj->getName())->pick() === $this->obj->pick(), 'addにメソッド指定時、戻り値のparentが自分自身ではありません。');
	}
	
	// removeの動作確認。removeParentsも内包される。children、parentsの基礎も。
	// ついでに軽くisEmptyも。
	public function TestofRemove() {
		$child = $this->obj->remove ('null');
		$this->assertIdentical($child->getName(), 'test', 'removeにnull指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('null')->isEmpty(), 'removeにnull指定時、childの戻り値がfalseではありません。');
		
		$child = $this->obj->remove ('scalar');
		$this->assertIdentical($child->getName(), 'test', 'removeに"abc"指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('scalar')->isEmpty(), 'removeに"abc"指定時、childの戻り値とremoveの戻り値が違います。');
		
		$child = $this->obj->remove ('array');
		$this->assertIdentical($child->getName(), 'test', 'removeに配列指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('array')->isEmpty(), 'removeに配列指定時、childの戻り値とremoveの戻り値が違います。');
		
		$child = $this->obj->remove ('object');
		$this->assertIdentical($child->getName(), 'test', 'removeにオブジェクト指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('object')->isEmpty(), 'removeにオブジェクト指定時、childの戻り値とremoveの戻り値が違います。');
		
		$child = $this->obj->remove ('function');
		$this->assertIdentical($child->getName(), 'test', 'removeに関数指定時、戻り値のgetNameの値が間違っています。');
		$this->assertTrue($this->obj->child('function')->isEmpty(), 'removeに関数指定時、childの戻り値とremoveの戻り値が違います。');
	}
	
	// childの動作確認。
	public function TestofChild() {
		// 形を作っておく。値は区別がつく程度に適当。
		$a = $this->obj->add ('a', 'value_a');
		$b = $a->add ('b', 'value_b');
		$c = $b->add ('c', 'value_c');
		$a2 = $this->obj->add ('a2', 'value_a2');
		$b2 = $a2->add ('b2', 'value_b2');
		$b22 = $a2->add ('b22', 'value_b22');
		$c2 = $b2->add ('c2', 'value_c2');
		$c22 = $b2->add ('c22', 'value_c22');
		$a3 = $this->obj->add ('a3', 'value_a3');
		$b3 = $a3->add ('b3', 'value_b3');
		$c3 = $b3->add ('c3', 'value_c3');
		
		// 文字列セレクタのテスト
		// 1段階
		$this->assertTrue($this->obj->child('a')->pick() === $a->pick(), 'child文字列指定1段階(a)の戻り値が違います。');
		$this->assertTrue($this->obj->child(' a2 ')->pick() === $a2->pick(), 'child文字列指定1段階(a2)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a3.')->pick() === $a3->pick(), 'child文字列指定1段階(a3)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a4')->isEmpty(), 'child文字列指定1段階(a4、存在しない)の戻り値が違います。');
		
		// 2段階
		$this->assertTrue($this->obj->child('a.b')->pick() === $b->pick(), 'child文字列指定2段階(a.b)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a2 . b2')->pick() === $b2->pick(), 'child文字列指定2段階(a2 . b2)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a3 . b3 .')->pick() === $b3->pick(), 'child文字列指定2段階(a3 . b3 .)の戻り値が違います。');
		$this->assertTrue($this->obj->child('b')->isEmpty(), 'child文字列指定1段階(b、2段階目に存在している)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a.b2')->isEmpty(), 'child文字列指定2段階(a.b2、存在しない)の戻り値が違います。');
		
		// 3段階
		$this->assertTrue($this->obj->child('a.b.c')->pick() === $c->pick(), 'child文字列指定2段階(a.b.c)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a2 . b2 . c2')->pick() === $c2->pick(), 'child文字列指定2段階(a2 . b2 . c2)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a3 . b3 . c3 .')->pick() === $c3->pick(), 'child文字列指定2段階(a3 . b3 . c3	.)の戻り値が違います。');
		$this->assertTrue($this->obj->child('b.c')->isEmpty(), 'child文字列指定2段階(b.c、3段階目に存在している)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a.b.c2')->isEmpty(), 'child文字列指定3段階(a.b.c2、存在しない)の戻り値が違います。');
		
		// 数値セレクタのテスト
		// 1段階
		$this->assertTrue($this->obj->child(0)->pick() === $a->pick(), 'child数値指定1段階(0)の戻り値が違います。');
		$this->assertTrue($this->obj->child(1)->pick() === $a2->pick(), 'child数値指定1段階(1)の戻り値が違います。');
		$this->assertTrue($this->obj->child(2)->pick() === $a3->pick(), 'child数値指定1段階(2)の戻り値が違います。');
		$this->assertTrue($this->obj->child(3)->isEmpty(), 'child数値指定1段階(3、存在しない)の戻り値が違います。');
		
		// 2段階
		$this->assertTrue($this->obj->child('0.0')->pick() === $b->pick(), 'child数値指定2段階(0.0)の戻り値が違います。');
		$this->assertTrue($this->obj->child('1.0')->pick() === $b2->pick(), 'child数値指定2段階(1.0)の戻り値が違います。');
		$this->assertTrue($this->obj->child('1.1.')->pick() === $b22->pick(), 'child数値指定2段階(1.1.)の戻り値が違います。');
		$this->assertTrue($this->obj->child('0.1')->isEmpty(), 'child数値指定2段階(0.1、存在しない)の戻り値が違います。');
		
		// 3段階
		$this->assertTrue($this->obj->child('0.0.0')->pick() === $c->pick(), 'child数値指定2段階(0.0.0)の戻り値が違います。');
		$this->assertTrue($this->obj->child('1.0.1')->pick() === $c22->pick(), 'child数値指定2段階(1.0.1)の戻り値が違います。');
		$this->assertTrue($this->obj->child('2.0.0.')->pick() === $c3->pick(), 'child数値指定2段階(2.0.0.)の戻り値が違います。');
		$this->assertTrue($this->obj->child('1.1.2')->isEmpty(), 'child数値指定3段階(1.1.2、存在しない)の戻り値が違います。');
		
		// 文字・数値混在
		$this->assertTrue($this->obj->child('a2.1')->pick() === $b22->pick(), 'child混在指定(a2.1)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a.0.')->pick() === $b->pick(), 'child混在指定(a.0.)の戻り値が違います。');
		$this->assertTrue($this->obj->child('a.1')->isEmpty(), 'child数値指定1段階(a.1、存在しない)の戻り値が違います。');
		
		// オブジェクトセレクタのテスト
		$this->assertTrue($this->obj->child($a)->pick() === $a->pick(), 'childオブジェクト指定($a)の戻り値が違います。');
		$this->assertTrue($this->obj->child($b)->isEmpty(), 'childオブジェクト指定($b、存在しない)の戻り値が違います。');
		
		// 戻り値の確認。
		$this->assertTrue(get_class($this->obj->child('a')) === 'YuTori', 'childの戻り値がYuToriではありません。');
		$this->assertTrue($this->obj->child('a')->getType() === 'scalar', 'childの戻り値のgetTypeが間違っています。');
	}
	
	// parentの動作確認。
	public function TestofParent() {
		// 形を作っておく。値は区別がつく程度に適当。
		$a = $this->obj->add ('a', 'value_a');
		$b = $a->add ('b', 'value_b');
		$c = $b->add ('c', 'value_c');
		$a2 = $this->obj->add ('a2', 'value_a2');
		$b2 = $a2->add ('b2', 'value_b2');
		$b22 = $a2->add ('b22', 'value_b22');
		$c2 = $b2->add ('c2', 'value_c2');
		$c22 = $b2->add ('c22', 'value_c22');
		$a3 = $this->obj->add ('a3', 'value_a3');
		$b3 = $a3->add ('b3', 'value_b3');
		$c3 = $b3->add ('c3', 'value_c3');
		
		// 文字列セレクタのテスト
		$this->assertTrue($a->parent('test')->pick() === $this->obj->pick(), 'parent文字列指定1段階の戻り値が違います。');
		$this->assertTrue($b->parent(' a ')->pick() === $a->pick(), 'parent文字列指定2段階の戻り値が違います。');
		$this->assertTrue($c->parent('a3')->isEmpty(), 'parent文字列指定、存在しない値の戻り値が違います。');
		
		// 数値セレクタのテスト
		
		$this->assertTrue($a->parent(0)->pick() === $this->obj->pick(), 'parent数値指定1段階の戻り値が違います。');
		$this->assertTrue($b->parent(0)->pick() === $a->pick(), 'parent数値指定2段階の戻り値が違います。');
		$this->assertTrue($a->parent(3)->isEmpty(), 'parent数値指定、存在しない値の戻り値が違います。');
		
		// オブジェクトセレクタのテスト
		$this->assertTrue($a->parent($this->obj)->pick() === $this->obj->pick(), 'parentオブジェクト指定の戻り値が違います。');
		$this->assertTrue($b->parent($c)->isEmpty(), 'parentオブジェクト指定、存在しない値の戻り値が違います。');
		
		// 戻り値の確認。selector型の試験自体はまた今度。
		$this->assertTrue(get_class($a->parent('test')) === 'YuTori', 'parentの戻り値がYuToriではありません。');
		$this->assertTrue($a->parent('test')->getType() === 'null', 'parentの戻り値のgetTypeが間違っています。');
	}

	// callの動作確認。
	public function TestofCall() {
		// nullのcallはnull返すだけ。
		$obj = YuTori::factory ('Test', null);
		$this->assertIdentical($obj->call(), null, 'null型のcallの戻り値が正しくありません。');

		$obj = YuTori::factory ('Test', 'test');
		$this->assertIdentical($obj->call(), 'test', 'scalar型のcallの戻り値が正しくありません。');

		$obj = YuTori::factory ('Test', 'myFunc', YuTori::TYPE_CALLABLE);
		$this->assertIdentical($obj->call(), 'func!', 'function型のcallの戻り値が正しくありません。');
		$this->assertIdentical($obj->call('arg'), 'arg', 'function型のcallに引数が渡されていません。');

	}

}


// object型の専用試験
class TestofYuTori_TypeofObject extends UnitTestCase {
	// インスタンス化
	public function __construct () {
		$this->ins = new myCls();
		$this->obj = YuTori::factory('test', $this->ins);
	}
	
	// get、setの動作確認
	public function TestofInstance() {
		$this->ins->flgReset ();
		
		$this->assertTrue($this->obj->get()->beforeGet, '_beforeGetメソッドが動作していません。');
		$this->assertTrue($this->obj->get()->afterGet, '_afterGetメソッドが動作していません。');
		$this->assertFalse($this->obj->get()->beforeSet, '_beforeSetメソッドが動作しています。');
		$this->assertFalse($this->obj->get()->afterSet, '_afterSetメソッドが動作しています。');
		$this->ins->flgReset ();
		
		$newObj = new stdClass ();
		$this->assertTrue($this->obj->set ($newObj) === $this->obj, 'setの戻り値が自分自身ではありません。');
		$this->assertIdentical($this->obj->get(), $newObj, 'setに同一の型（オブジェクト）を指定しても値が変更されませんでした。');
		
		$this->obj->set ($this->ins);
		
		$this->assertTrue($this->obj->get()->beforeGet, '_beforeGetメソッドが動作していません。');
		$this->assertTrue($this->obj->get()->afterGet, '_afterGetメソッドが動作していません。');
		$this->assertTrue($this->obj->get()->beforeSet, '_beforeSetメソッドが動作していません。');
		$this->assertTrue($this->obj->get()->afterSet, '_afterSetメソッドが動作していません。');
		$this->ins->flgReset ();
		
		// 紛らわしいので戻す
		$this->obj->set($this->ins);
	}
	
	// callの動作確認。
	public function TestofCall() {
		$this->obj->set($this->ins);
		$this->assertIdentical($this->obj->call(), 'call!', 'callの戻り値がcallメソッドの戻り値ではありません。');
		$this->ins->flgReset ();
		$this->assertIdentical($this->obj->call('called'), 'call!', 'callの戻り値がcallメソッドの戻り値ではありません。');
		$this->assertIdentical($this->ins->callRes, 'called', 'callに引数が渡されていません。');
	}
}

// array型の専用試験
class TestofYuTori_TypeofArray extends UnitTestCase {
	// インスタンス化
	public function __construct () {
		$this->arr = array('a', 'b', 'asc'=>'c', 'obj'=>new myCls());
		$this->obj = YuTori::factory('test', $this->arr);
	}
	
	// childの動作確認。set時に全配下が生成されているか。
	public function TestofChild() {
		$this->assertTrue($this->obj->child('0')->get() === 'a', 'array型の配下に値がありません。(0)');
		$this->assertTrue($this->obj->child('1')->get() === 'b', 'array型の配下に値がありません。(0)');
		$this->assertTrue($this->obj->child('asc')->get() === 'c', 'array型の配下に値がありません。(0)');
	}
	
	// callの動作確認。
	// callはchildren全部にcallをかけ、その結果を配列として返す。
	public function TestofCall() {
		$this->assertIdentical($this->obj->call(), array('a', 'b', 'asc'=>'c', 'obj'=>'call!'), 'callの戻り値が正しくありません。');
	}
}

// 基幹関数Yのテスト
class TestofY extends UnitTestCase {
	public function TestofInstance() {
		// 二度呼び出して同じものかどうか
		$a = Y();
		$b = Y();
		$this->assertTrue($a === $b, 'Y()を二度呼び出した場合、別の参照になっています。');
		$this->assertIdentical($a->getName(), 'Root', 'getNypeが間違っています。');
		$this->assertIdentical($a->getType(), 'null', 'getTypeが間違っています。');
	}
	
	// addした後childセレクタを引数とできるか
	public function TestofChild() {
		// 形を作っておく。値は区別がつく程度に適当。
		$a = Y()->add ('a', 'value_a');
		$b = $a->add ('b', 'value_b');
		$c = $b->add ('c', 'value_c');
		$a2 = Y()->add ('a2', 'value_a2');
		$b2 = $a2->add ('b2', 'value_b2');
		$b22 = $a2->add ('b22', 'value_b22');
		$c2 = $b2->add ('c2', 'value_c2');
		$c22 = $b2->add ('c22', 'value_c22');
		$a3 = Y()->add ('a3', 'value_a3');
		$b3 = $a3->add ('b3', 'value_b3');
		$c3 = $b3->add ('c3', 'value_c3');
		
		// 文字列セレクタのテスト
		// 1段階
		$this->assertTrue(Y('a')->pick() === $a->pick(), 'child文字列指定1段階(a)の戻り値が違います。');
		$this->assertTrue(Y(' a2 ')->pick() === $a2->pick(), 'child文字列指定1段階(a2)の戻り値が違います。');
		$this->assertTrue(Y('a3.')->pick() === $a3->pick(), 'child文字列指定1段階(a3.)の戻り値が違います。');
		$this->assertTrue(Y('a4')->isEmpty(), 'child文字列指定1段階(a4、存在しない)の戻り値が違います。');
		
		// 2段階
		$this->assertTrue(Y('a.b')->pick() === $b->pick(), 'child文字列指定2段階(a.b)の戻り値が違います。');
		$this->assertTrue(Y('a2 . b2')->pick() === $b2->pick(), 'child文字列指定2段階(a2 . b2)の戻り値が違います。');
		$this->assertTrue(Y('a3 . b3 .')->pick() === $b3->pick(), 'child文字列指定2段階(a3 . b3 .)の戻り値が違います。');
		$this->assertTrue(Y('b')->isEmpty(), 'child文字列指定1段階(b、2段階目に存在している)の戻り値が違います。');
		$this->assertTrue(Y('a.b2')->isEmpty(), 'child文字列指定2段階(a.b2、存在しない)の戻り値が違います。');
		
		// 3段階
		$this->assertTrue(Y('a.b.c')->pick() === $c->pick(), 'child文字列指定2段階(a.b.c)の戻り値が違います。');
		$this->assertTrue(Y('a2 . b2 . c2')->pick() === $c2->pick(), 'child文字列指定2段階(a2 . b2 . c2)の戻り値が違います。');
		$this->assertTrue(Y('a3 . b3 . c3 .')->pick() === $c3->pick(), 'child文字列指定2段階(a3 . b3 . c3 .)の戻り値が違います。');
		$this->assertTrue(Y('b.c')->isEmpty(), 'child文字列指定2段階(b.c、3段階目に存在している)の戻り値が違います。');
		$this->assertTrue(Y('a.b.c2')->isEmpty(), 'child文字列指定3段階(a.b.c2、存在しない)の戻り値が違います。');
		
		// 数値セレクタのテスト
		// 1段階
		$this->assertTrue(Y(0)->pick() === $a->pick(), 'child数値指定1段階(0)の戻り値が違います。');
		$this->assertTrue(Y(1)->pick() === $a2->pick(), 'child数値指定1段階(1)の戻り値が違います。');
		$this->assertTrue(Y(2)->pick() === $a3->pick(), 'child数値指定1段階(2)の戻り値が違います。');
		$this->assertTrue(Y(3)->isEmpty(), 'child数値指定1段階(3、存在しない)の戻り値が違います。');
		
		// 2段階
		$this->assertTrue(Y('0.0')->pick() === $b->pick(), 'child数値指定2段階(0.0)の戻り値が違います。');
		$this->assertTrue(Y('1.0')->pick() === $b2->pick(), 'child数値指定2段階(1.0)の戻り値が違います。');
		$this->assertTrue(Y('1.1.')->pick() === $b22->pick(), 'child数値指定2段階(1.1.)の戻り値が違います。');
		$this->assertTrue(Y('0.1')->isEmpty(), 'child数値指定2段階(0.1、存在しない)の戻り値が違います。');
		
		// 3段階
		$this->assertTrue(Y('0.0.0')->pick() === $c->pick(), 'child数値指定2段階(0.0.0)の戻り値が違います。');
		$this->assertTrue(Y('1.0.1')->pick() === $c22->pick(), 'child数値指定2段階(1.0.1)の戻り値が違います。');
		$this->assertTrue(Y('2.0.0.')->pick() === $c3->pick(), 'child数値指定2段階(2.0.0.)の戻り値が違います。');
		$this->assertTrue(Y('1.1.2')->isEmpty(), 'child数値指定3段階(1.1.2、存在しない)の戻り値が違います。');
		
		// 文字・数値混在
		$this->assertTrue(Y('a2.1')->pick() === $b22->pick(), 'child混在指定(a2.1)の戻り値が違います。');
		$this->assertTrue(Y('a.0.')->pick() === $b->pick(), 'child混在指定(a.0.)の戻り値が違います。');
		$this->assertTrue(Y('a.1')->isEmpty(), 'child数値指定1段階(a.1、存在しない)の戻り値が違います。');
		
		// オブジェクトセレクタのテスト
		$this->assertTrue(Y($a)->pick() === $a->pick(), 'childオブジェクト指定($a)の戻り値が違います。');
		$this->assertTrue(Y($b)->isEmpty(), 'childオブジェクト指定($b、存在しない)の戻り値が違います。');
		
		// 戻り値の確認。
		$this->assertTrue(Y('a')->getType() === 'scalar', 'childの戻り値のgetTypeが間違っています。');
	}
}

// 比較系メソッドの試験
class TestofYuToriCompression extends UnitTestCase {
	public function __construct () {
		$this->arrObj = array(
			'Null'=>null,
			'Scalar'=>'Hello World!',
			'Array'=>array('a', 'b', 'c'),
			'Object'=>new myCls(),
		);
	}

	// isEmptyの試験
	public function TestofisEmpty() {
		$objEmp = YuTori::factory ('', null, 'Empty');
		$this->assertTrue($objEmp->isEmpty(), '空オブジェクトに対してisEmptyがtrueを返していません。');
		
		foreach ($this->arrObj as $Key=>$Val) {
			$obj = YuTori::factory($Key, $Val);
			$this->assertFalse($obj->isEmpty(), $Key.'型の時、isEmptyがfalseを返していません。');
		}
	}
	
	// eq、seqの試験
	// このへんからエラー文の法則変更…。
	public function TestofEq() {
		$objEmp = YuTori::factory ('', null, 'Empty');
		$this->assertTrue($objEmp->eq(null), '空オブジェクトとnullのeq比較');
		$this->assertTrue($objEmp->seq(null), '空オブジェクトとnullのseq比較');
		$this->assertFalse($objEmp->eq('test'), '空オブジェクトと文字列のeq比較');
		$this->assertFalse($objEmp->seq('test'), '空オブジェクトと文字列のseq比較');
		$this->assertTrue($objEmp->eq(''), '空オブジェクトと空文字列のeq比較');
		$this->assertFalse($objEmp->seq(''), '空オブジェクトと空文字列のseq比較');
		$this->assertTrue($objEmp->eq(0), '空オブジェクトと0のeq比較');
		$this->assertFalse($objEmp->seq(0), '空オブジェクトと0のseq比較');

		$objEmp = YuTori::factory ('test', null);
		$this->assertTrue($objEmp->eq(null), 'null型とnullのeq比較');
		$this->assertTrue($objEmp->seq(null), 'null型とnullのseq比較');
		$this->assertFalse($objEmp->eq('test'), 'null型と文字列のeq比較');
		$this->assertFalse($objEmp->seq('test'), 'null型と文字列のseq比較');
		$this->assertTrue($objEmp->eq(''), 'null型と空文字列のeq比較');
		$this->assertFalse($objEmp->seq(''), 'null型と空文字列のseq比較');
		$this->assertTrue($objEmp->eq(0), 'null型と0のeq比較');
		$this->assertFalse($objEmp->seq(0), 'null型と0のseq比較');

		$objEmp = YuTori::factory ('test', 'test');
		$this->assertFalse($objEmp->eq(null), 'scalar型とnullのeq比較');
		$this->assertFalse($objEmp->eq(null), 'scalar型とnullのseq比較');
		$this->assertTrue($objEmp->eq('test'), 'scalar型と文字列のeq比較');
		$this->assertTrue($objEmp->seq('test'), 'scalar型と文字列のseq比較');
		$this->assertFalse($objEmp->eq('test2'), 'scalar型と異なる文字列のeq比較');
		$this->assertFalse($objEmp->seq('test2'), 'scalar型と異なる文字列のseq比較');
		$this->assertFalse($objEmp->eq(''), 'scalar型と空文字列のeq比較');
		$this->assertFalse($objEmp->seq(''), 'scalar型と空文字列のseq比較');
		$this->assertTrue($objEmp->eq(0), 'scalar型と0のeq比較'); // 'test' == 0 はtrueになる。
		$this->assertFalse($objEmp->seq(0), 'scalar型と0のseq比較');

		$objEmp = YuTori::factory ('test', '0');
		$this->assertFalse($objEmp->eq(null), '文字列のゼロを入れたscalar型とnullのeq比較');
		$this->assertFalse($objEmp->eq(null), '文字列のゼロを入れたscalar型とnullのseq比較');
		$this->assertTrue($objEmp->eq('0'), '文字列のゼロを入れたscalar型と文字列のeq比較');
		$this->assertTrue($objEmp->seq('0'), '文字列のゼロを入れたscalar型と文字列のseq比較');
		$this->assertFalse($objEmp->eq('test2'), '文字列のゼロを入れたscalar型と異なる文字列のeq比較');
		$this->assertFalse($objEmp->seq('test2'), '文字列のゼロを入れたscalar型と異なる文字列のseq比較');
		$this->assertFalse($objEmp->eq(''), '文字列のゼロを入れたscalar型と空文字列のeq比較');
		$this->assertFalse($objEmp->seq(''), '文字列のゼロを入れたscalar型と空文字列のseq比較');
		$this->assertTrue($objEmp->eq(0), '文字列のゼロを入れたscalar型と0のeq比較');
		$this->assertFalse($objEmp->seq(0), '文字列のゼロを入れたscalar型と0のseq比較');

		$obj = new stdClass();
		$objCmp1 = YuTori::factory ('test', $obj);
		$objCmp2 = new stdClass();
		$objCmp3 = new myCls();
		$this->assertTrue($objCmp1->eq($obj), '同じ参照のオブジェクトとeq');
		$this->assertTrue($objCmp1->seq($obj), '同じ参照のオブジェクトとseq');
		$this->assertTrue($objCmp1->eq($objCmp2), '同じ内容を持ったオブジェクトとeq');
		$this->assertFalse($objCmp1->seq($objCmp2), '同じ内容を持ったオブジェクトとseq');
		$this->assertFalse($objCmp1->eq($objCmp3), '別のオブジェクトとseq');
		$this->assertFalse($objCmp1->seq($objCmp3), '別のオブジェクトとseq');

		$objCmp1 = YuTori::factory ('test', new myCls());
		$this->assertTrue($objCmp1->eq('call!'), 'callを持ったオブジェクトとeq');
		$this->assertTrue($objCmp1->seq('call!'), 'callを持ったオブジェクトとseq');
		$this->assertFalse($objCmp1->eq('call'), 'callを持ったオブジェクトと違う値をeq');
		$this->assertFalse($objCmp1->seq('call'), 'callを持ったオブジェクトと違う値をseq');

		$objCmp1 = YuTori::factory ('test', 'test');
		$objCmp2 = YuTori::factory ('test1', 'test');
		$objCmp3 = YuTori::factory ('test2', 'test2');
		$this->assertTrue($objCmp1->eq($objCmp2), '同じ内容を持った別のYuToriとeq');
		$this->assertTrue($objCmp1->seq($objCmp2), '同じ内容を持った別のYuToriとseq');
		$this->assertFalse($objCmp1->eq($objCmp3), '違う内容を持った別のYuToriとeq');
		$this->assertFalse($objCmp1->seq($objCmp3), '違う内容を持った別のYuToriとseq');
	}
}

// 出力系メソッドの試験
class TestofYuToriOutput extends UnitTestCase {
	// putの試験
 	public function TestofPut() {
		$objStr = YuTori::factory ('test', 'test');

		ob_start ();
		$objStr->put(); $res = ob_get_contents(); ob_end_clean();
		$this->assertIdentical($res, 'test', '無指定のput');
		ob_start ();
		$objStr->set (null);
		$objStr->put('NULL'); $res = ob_get_contents(); ob_end_clean();
		$this->assertIdentical($res, 'NULL', 'null時のput');
		ob_start ();
		$objStr->set (5);
		$objStr->put(null, '%03d'); $res = ob_get_contents(); ob_end_clean();
		$this->assertIdentical($res, '005', 'フォーマット指定のput');
	}
}